#' Display regression model results in table
#'
#' This function takes a regression model object and returns a formatted table
#' that is publication-ready. The function is highly customizable
#' allowing the user to obtain a bespoke summary table of the
#' regression model results. Review the
#' \href{http://www.danieldsjoberg.com/gtsummary/articles/tbl_regression.html}{tbl_regression vignette}
#' for detailed examples.
#'
#' @section Methods:
#'
#' The default method for `tbl_regression()` model summary uses `broom::tidy(x)`
#' to perform the initial tidying of the model object. There are, however,
#' a few models that use [modifications][tbl_regression_methods].
#'
#' - `"parsnip/workflows"`: If the model was prepared using parsnip/workflows,
#' the original model fit is extracted and the original `x=` argument
#' is replaced with the model fit. This will typically go unnoticed; however,if you've
#' provided a custom tidier in `tidy_fun=` the tidier will be applied to the model
#' fit object and not the parsnip/workflows object.
#' - `"survreg"`: The scale parameter is removed, `broom::tidy(x) %>% dplyr::filter(term != "Log(scale)")`
#' - `"multinom"`: This multinomial outcome is complex, with one line per covariate per outcome (less the reference group)
#' - `"gam"`: Uses the internal tidier `tidy_gam()` to print both parametric and smooth terms.
#' - `"lmerMod"`, `"glmerMod"`, `"glmmTMB"`, `"glmmadmb"`, `"stanreg"`, `"brmsfit"`: These mixed effects
#' models use `broom.mixed::tidy(x, effects = "fixed")`. Specify `tidy_fun = broom.mixed::tidy`
#' to print the random components.
#'
#' @section Note:
#' The N reported in the output is the number of observations
#' in the data frame `model.frame(x)`. Depending on the model input, this N
#' may represent different quantities. In most cases, it is the number of people or
#' units in your model.  Here are some common exceptions.
#' 1. Survival regression models including time dependent covariates.
#' 2. Random- or mixed-effects regression models with clustered data.
#' 3. GEE regression models with clustered data.
#'
#' This list is not exhaustive, and care should be taken for each number reported.
#' @param x Regression model object
#' @param exponentiate Logical indicating whether to exponentiate the
#' coefficient estimates. Default is `FALSE`.
#' @param label List of formulas specifying variables labels,
#' e.g. `list(age ~ "Age", stage ~ "Path T Stage")`
#' @param include Variables to include in output. Input may be a vector of
#' quoted variable names, unquoted variable names, or tidyselect select helper
#' functions. Default is `everything()`.
#' @param conf.level Must be strictly greater than 0 and less than 1.
#' Defaults to 0.95, which corresponds to a 95 percent confidence interval.
#' @param intercept Logical argument indicating whether to include the intercept
#' in the output.  Default is `FALSE`
#' @param show_single_row By default categorical variables are printed on
#' multiple rows.  If a variable is dichotomous (e.g. Yes/No) and you wish to print
#' the regression coefficient on a single row, include the variable name(s)
#' here--quoted and unquoted variable name accepted.
#' @param estimate_fun Function to round and format coefficient estimates.
#' Default is [style_sigfig] when the coefficients are not transformed, and
#' [style_ratio] when the coefficients have been exponentiated.
#' @param pvalue_fun Function to round and format p-values.
#' Default is [style_pvalue].
#' The function must have a numeric vector input (the numeric, exact p-value),
#' and return a string that is the rounded/formatted p-value (e.g.
#' `pvalue_fun = function(x) style_pvalue(x, digits = 2)` or equivalently,
#'  `purrr::partial(style_pvalue, digits = 2)`).
#' @param tidy_fun Option to specify a particular tidier function for the
#' model. Default is to use `broom::tidy`, but if an error occurs
#' then tidying of the model is attempted with `parameters::model_parameters()`,
#' if installed.
#' @param add_estimate_to_reference_rows add a reference value. Default is FALSE
#' @param ... Not used
#' @param exclude DEPRECATED
#' @param show_yesno DEPRECATED
#' @author Daniel D. Sjoberg
#' @seealso See tbl_regression \href{http://www.danieldsjoberg.com/gtsummary/articles/tbl_regression.html}{vignette} for detailed examples
#' @family tbl_regression tools
#' @export
#' @rdname tbl_regression
#' @return A `tbl_regression` object
#' @examples
#' # Example 1 ----------------------------------
#' library(survival)
#' tbl_regression_ex1 <-
#'   coxph(Surv(ttdeath, death) ~ age + marker, trial) %>%
#'   tbl_regression(exponentiate = TRUE)
#'
#' # Example 2 ----------------------------------
#' tbl_regression_ex2 <-
#'   glm(response ~ age + grade, trial, family = binomial(link = "logit")) %>%
#'   tbl_regression(exponentiate = TRUE)
#'
#' # Example 3 ----------------------------------
#' suppressMessages(library(lme4))
#' tbl_regression_ex3 <-
#'   glmer(am ~ hp + (1 | gear), mtcars, family = binomial) %>%
#'   tbl_regression(exponentiate = TRUE)
#' @section Example Output:
#' \if{html}{Example 1}
#'
#' \if{html}{\figure{tbl_regression_ex1.png}{options: width=64\%}}
#'
#' \if{html}{Example 2}
#'
#' \if{html}{\figure{tbl_regression_ex2.png}{options: width=50\%}}
#'
#' \if{html}{Example 3}
#'
#' \if{html}{\figure{tbl_regression_ex3.png}{options: width=50\%}}

tbl_regression <- function(x, ...) {
  UseMethod("tbl_regression")
}

#' @export
#' @rdname tbl_regression
tbl_regression.default <- function(x, label = NULL, exponentiate = FALSE,
                                   include = everything(), show_single_row = NULL,
                                   conf.level = NULL, intercept = FALSE,
                                   estimate_fun = NULL, pvalue_fun = NULL,
                                   tidy_fun = NULL,
                                   add_estimate_to_reference_rows = FALSE,
                                   show_yesno = NULL, exclude = NULL, ...) {
  # deprecated arguments -------------------------------------------------------
  if (!is.null(show_yesno)) {
    lifecycle::deprecate_stop(
      "1.2.2", "tbl_regression(show_yesno = )",
      "tbl_regression(show_single_row = )"
    )
  }

  if (!rlang::quo_is_null(rlang::enquo(exclude))) {
    lifecycle::deprecate_stop(
      "1.2.5",
      "gtsummary::tbl_regression(exclude = )",
      "tbl_regression(include = )",
      details = paste0(
        "The `include` argument accepts quoted and unquoted expressions similar\n",
        "to `dplyr::select()`. To exclude variable, use the minus sign.\n",
        "For example, `include = -c(age, stage)`"
      )
    )
  }

  # setting defaults -----------------------------------------------------------
  tidy_fun <- tidy_fun %||% broom.helpers::tidy_with_broom_or_parameters
  pvalue_fun <-
    pvalue_fun %||%
    get_theme_element("tbl_regression-arg:pvalue_fun") %||%
    get_theme_element("pkgwide-fn:pvalue_fun") %||%
    getOption("gtsummary.pvalue_fun", default = style_pvalue) %>%
    gts_mapper("tbl_regression(pvalue_fun=)")
  estimate_fun <-
    estimate_fun %||%
    get_theme_element("tbl_regression-arg:estimate_fun") %||%
    getOption(
      "gtsummary.tbl_regression.estimate_fun",
      default = ifelse(exponentiate == TRUE, style_ratio, style_sigfig)
    ) %>%
    gts_mapper("tbl_regression(estimate_fun=)")
  conf.level <-
    conf.level %||%
    get_theme_element("tbl_regression-arg:conf.level") %||%
    getOption("gtsummary.conf.level", default = 0.95)
  add_estimate_to_reference_rows <-
    add_estimate_to_reference_rows %||%
    get_theme_element("tbl_regression-arg:add_estimate_to_reference_rows", default = FALSE)


  # checking estimate_fun and pvalue_fun are functions
  if (!purrr::every(list(estimate_fun, pvalue_fun, tidy_fun %||% pvalue_fun), is.function)) {
    stop("Inputs `estimate_fun`, `pvalue_fun`, `tidy_fun` must be functions.",
      call. = FALSE
    )
  }

  include <- rlang::enquo(include)
  show_single_row <- rlang::enquo(show_single_row)

  # will return call, and all object passed to in tbl_regression call
  # the object func_inputs is a list of every object passed to the function
  func_inputs <- as.list(environment())

  table_body <-
    tidy_prep(x,
      tidy_fun = tidy_fun, exponentiate = exponentiate,
      conf.level = conf.level, intercept = intercept,
      label = label, show_single_row = !!show_single_row,
      include = !!include,
      add_estimate_to_reference_rows = add_estimate_to_reference_rows
    )

  # saving evaluated `label`, `show_single_row`, and `include` -----------------
  func_inputs$label <-
    .formula_list_to_named_list(
      x = label,
      var_info = table_body,
      arg_name = "label"
    )

  func_inputs$show_single_row <-
    .select_to_varnames(
      select = !!show_single_row,
      var_info = table_body,
      arg_name = "show_single_row"
    )

  func_inputs$include <- unique(table_body$variable)

  # adding character CI
  if (all(c("conf.low", "conf.high") %in% names(table_body))) {
    ci.sep <- get_theme_element("pkgwide-str:ci.sep", default = ", ")
    table_body <-
      table_body %>%
      mutate( # adding character CI
        ci = if_else(
          !is.na(.data$conf.low),
          paste0(estimate_fun(.data$conf.low), ci.sep, estimate_fun(.data$conf.high)),
          NA_character_
        )
      ) %>%
      dplyr::relocate(any_of("ci"), .after = "conf.high")
  }

  # re-ordering columns
  table_body <-
    table_body %>%
    dplyr::relocate(any_of(c("conf.low", "conf.high", "ci", "p.value")), .after = last_col())

  # table of column headers
  x <-
    .create_gtsummary_object(
      table_body = table_body,
      N = pluck(table_body, "N_obs", 1),
      n = pluck(table_body, "N_obs", 1), # i want to remove this eventually
      N_event = pluck(table_body, "N_event", 1), model_obj = x,
      inputs = func_inputs,
      call_list = list(tbl_regression = match.call())
    ) %>%
    purrr::discard(is.null)

  # assigning a class of tbl_regression (for special printing in R markdown)
  class(x) <- c("tbl_regression", "gtsummary")

  # setting column headers, and print instructions
  tidy_columns_to_report <-
    get_theme_element("tbl_regression-chr:tidy_columns",
      default = c("conf.low", "conf.high", "p.value")
    ) %>%
    union("estimate") %>%
    intersect(names(table_body))

  # setting default table_header values
  x <-
    .tbl_regression_default_table_header(
      x,
      exponentiate = exponentiate,
      tidy_columns_to_report = tidy_columns_to_report,
      estimate_fun = estimate_fun,
      pvalue_fun = pvalue_fun,
      conf.level = conf.level
    )

  # running any additional mods ------------------------------------------------
  x <-
    get_theme_element("tbl_regression-fn:addnl-fn-to-run", default = identity) %>%
    do.call(list(x))

  # return results -------------------------------------------------------------
  x
}
